package rpg.map;

import java.util.ArrayList;

import rpg.ai.pathFinder.APathFinder;
import rpg.ai.pathFinder.PathFinder;
import square.data.ItemData;
import square.data.LocationData;

/**
 * 
 * @author Pedro Silva Moreira
 */
public class Location extends LocationData {

	private static PathFinder pathFinder = new APathFinder();
	private Creature movingCreature = null;

	// Generator
	public Location(Map map, int x, int y, int z) {
		super(map, x, y, z);
	}

	// Search
	private Item nearItemValid(Location location, String name) {
		if (location != null) {
			return location.containsItem(name);
		}

		return null;
	}

	private Creature nearCreatureValid(Location location, String name,
			boolean player) {
		if (location != null && location.getCreature() != null) {
			if (name.isEmpty() || location.getCreature().getName().equals(name)) {
				Creature result = (Creature) location.getCreature().getOwner();
				if (!player || result.getPlayer() != null)
					return result;
			}
		}

		return null;
	}

	public Item nearItem(String name, int range) {
		Map map = getMap();
		if (map == null)
			return null;

		int x = getX();
		int y = getY();
		int z = getZ();

		Item result;
		for (int i = 1; i <= range; i++) {
			// Top
			Location top = map.getLocation(x, y - i, z);
			result = nearItemValid(top, name);
			if (result != null)
				return result;

			// Bottom
			Location bottom = map.getLocation(x, y + i, z);
			result = nearItemValid(bottom, name);
			if (result != null)
				return result;

			// Right
			for (int j = 0; j < i; j++) {
				// Top & Right
				Location topRight = map.getLocation(x + i - j, y - j, z);
				result = nearItemValid(topRight, name);
				if (result != null)
					return result;

				if (j == 0)
					continue;

				// Bottom
				Location bottomRight = map.getLocation(x + i - j, y + j, z);
				result = nearItemValid(bottomRight, name);
				if (result != null)
					return result;
			}

			// Left
			for (int j = 0; j < i; j++) {
				// Top & Left
				Location topLeft = map.getLocation(x - i + j, y - j, z);
				result = nearItemValid(topLeft, name);
				if (result != null)
					return result;

				if (j == 0)
					continue;

				// Bottom
				Location bottomLeft = map.getLocation(x - i + j, y + j, z);
				result = nearItemValid(bottomLeft, name);
				if (result != null)
					return result;
			}
		}
		return null;
	}

	public Creature nearCreature(String name, int range, boolean player) {
		Map map = getMap();
		if (map == null)
			return null;

		int x = getX();
		int y = getY();
		int z = getZ();

		Creature result;
		for (int i = 1; i <= range; i++) {
			// Top
			Location top = map.getLocation(x, y - i, z);
			result = nearCreatureValid(top, name, player);
			if (result != null)
				return result;

			// Bottom
			Location bottom = map.getLocation(x, y + i, z);
			result = nearCreatureValid(bottom, name, player);
			if (result != null)
				return result;

			// Right
			for (int j = 0; j < i; j++) {
				// Top & Right
				Location topRight = map.getLocation(x + i - j, y - j, z);
				result = nearCreatureValid(topRight, name, player);
				if (result != null)
					return result;

				if (j == 0)
					continue;

				// Bottom
				Location bottomRight = map.getLocation(x + i - j, y + j, z);
				result = nearCreatureValid(bottomRight, name, player);
				if (result != null)
					return result;
			}

			// Left
			for (int j = 0; j < i; j++) {
				// Top & Left
				Location topLeft = map.getLocation(x - i + j, y - j, z);
				result = nearCreatureValid(topLeft, name, player);
				if (result != null)
					return result;

				if (j == 0)
					continue;

				// Bottom
				Location bottomLeft = map.getLocation(x - i + j, y + j, z);
				result = nearCreatureValid(bottomLeft, name, player);
				if (result != null)
					return result;
			}
		}
		return null;
	}

	// Artificial Intelligence
	public ArrayList<Location> pathTo(Location location, boolean walkAble,
			boolean one, boolean escape) {
		if (!escape) {
			int distance = distance(location, false);
			if (distance == 0 || distance == 1 && !location.walkAble()) {
				return new ArrayList<Location>();
			}
		}

		if (!walkAble) {
			ArrayList<Location> result = new ArrayList<Location>();

			if (location.getX() > this.getX()) {
				result.add(getMap().getLocation(getX() + 1, getY(), getZ()));
			} else if (location.getX() < this.getX()) {
				result.add(getMap().getLocation(getX() - 1, getY(), getZ()));
			} else if (location.getY() > this.getY()) {
				result.add(getMap().getLocation(getX(), getY() + 1, getZ()));
			} else {
				result.add(getMap().getLocation(getX(), getY() - 1, getZ()));
			}
			return result;
		}

		return getPathFinder().pathTo(this, location, one, escape);
	}

	// Shortcut
	@Override
	public Map getMap() {
		return (Map) super.getMap();
	}

	public int distance(Location from, boolean max) {
		int distanceX = Math.abs(this.getX() - from.getX());
		int distanceY = Math.abs(this.getY() - from.getY());

		if (max) {
			if (distanceX > distanceY)
				return distanceX;
			return distanceY;
		}
		return distanceX + distanceY;
	}

	public Boolean walkAble() {		
		if (this.getCreature() != null || this.getMovingCreature() != null) {
			return false;
		}

		boolean walkAble = false;
		for (ItemData it : this.getItems()) {
			if (it.getWalkAble() == false) {
				return false;
			} else {
				walkAble = true;
			}
		}

		return walkAble;
	}

	public int stackCount() {
		int stack = 0;
		for (ItemData it : this.getItems()) {
			stack += it.getStack();
		}
		return stack;
	}

	public Item containsItem(String name) {
		for (ItemData it : this.getItems()) {
			if (it.getName() != null && it.getName().equals(name)) {
				return (Item) it.getOwner();
			}
		}
		return null;
	}
	
	public Item containsItemType(String type) {
		for(ItemData it : this.getItems()) {
			if(it.getType().equals(type))
				return (Item) it.getOwner();
		}
		return null;
	}

	// Item Control
	public void addItem(Item item) {
		boolean ground = item.getData().getType().equals("ground");
		if (this.getItems().size() == 0 && !ground)
			return;

		// Replace Ground
		if (ground && this.getItems().size() > 0) {
			Item currentGround = (Item) this.getItems().get(0).getOwner();
			this.getItems().remove(0);
			currentGround.setLocation(null);
		}
		
		// Replace Special
		boolean special = item.getData().getType().equals("special");
		if (special && this.getItems().size() > 0) {
			
			Item currentSpecial = this.containsItemType("special");
			if(currentSpecial != null) {
				this.getItems().remove(currentSpecial);
				currentSpecial.setLocation(null);
			}				
		}

		// Remove From Old Position
		if (item.getLocation() != null) {
			item.getLocation().removeItem(item);
		}

		if (ground)
			this.getItems().add(0, item.getData());
		else
			this.getItems().add(item.getData());

		if (this.getCreature() != null)
			item.onStepIn((Creature) this.getCreature().getOwner());

		item.setLocation(this);
	}

	public void removeItem(Item item) {
		boolean ground = item.getData().getType().equals("ground");
		if (ground && getItems().size() > 1)
			return;

		if (this.getItems().remove(item.getData())) {
			item.setLocation(null);
		}
	}

	// Creature Control
	public boolean setCreature(Creature creature) {
		if (this.getItems().size() == 0)
			return false;

		if (creature == null) {
			if (this.getCreature() == null) {
				this.setMovingCreature(null);
				return true;
			}

			for (ItemData it : this.getItems()) {
				((Item) it.getOwner()).onStepOut((MapObject) this.getCreature()
						.getOwner());
			}
			this.creature = null;
			return true;
		}

		if (this.creature != null) {
			return false;
		}

		if (creature.getLocation() != null) {
			creature.getLocation().setCreature(null);
		}

		for (ItemData it : this.getItems()) {
			((Item) it.getOwner()).onStepIn(creature);
		}

		creature.setLocation(this);
		this.creature = creature.getData();
		return true;
	}

	// Effect Control
	public void addEffect(Effect effect) {
		addEffect(effect, this);
	}

	public void addEffect(Effect effect, Location destiny) {
		if (effect.getLocation() != null) {
			effect.getLocation().removeEffect(effect);
		}

		this.getEffects().add(effect.getData());
		effect.setLocation(this);
		effect.onUse(destiny);
	}

	public void removeEffect(Effect effect) {
		if (this.getEffects().remove(effect.getData())) {
			effect.setLocation(null);
		}
	}

	// Get & Set
	public static PathFinder getPathFinder() {
		return pathFinder;
	}

	public Creature getMovingCreature() {
		return movingCreature;
	}

	public void setMovingCreature(Creature movingCreature) {
		this.movingCreature = movingCreature;
	}
}
